/**
*  \file xmem.h
*
*  \brief   Inverse memory handling functions.
*
*  \author  Michael J. Hohmann <michael.hohmann@linde-mh.de>
*           Francesco D'Aprea <francesco.daprea@linde-mh.de>
*
*/

#ifndef XMEM_H_INCLUDED
#define XMEM_H_INCLUDED

/*================================================[ public includes  ]================================================*/
#include "../cfg/prj.h"

/*================================================[ public defines   ]================================================*/

/*================================================[ public datatypes ]================================================*/

/*================================================[ public variables ]================================================*/

/*================================================[ public functions ]================================================*/

/** Inverse Memory Copy.
*
*  The function xmemcpy copies size bytes from the object pointed to by src into
*  th object pointed to by dest with inverse order. If copying takes place between
*  objects that overlap, the behavior is undefined.
*
*  \param dest Pointer to the destination object. It's not save to pass NULL.
*  \param src  Pointer to the source object. It's not save to pass NULL.
*  \param size Number of bytes to copy.
*
*  \return The xmemcpy function returns the value of dest.
*/
STATIC_INLINE void * xmemcpy( void * RESTRICT dest, void const * RESTRICT src, U32 size )
{
   U8 const *s = (U8 *) src;
   U8       *d = (U8 *)dest;

   d += size;

   while ( size-- )
   {
      d--;
      *d = *s;
      s++;
   }
   return dest;
}

/** Inverse Memory Compare.
*
*  The function xmemcmp compares size bytes from the object pointed to by ptr1 with
*  size bytes from the object pointed to by ptr2 with inverse order.
*
*  \param ptr1 Pointer to the first object. It's not save to pass NULL.
*  \param ptr2 Pointer to the second object. It's not save to pass NULL.
*  \param size Number of bytes to compare.
*
*  \return The xmemcmp function returns an unsigned value indicating the relationship between the content of the
*          objects:
*          A zero value indicates that the contents of both objects are inverted or passed size is zero.
*          A value greater than zero indicates that the first byte that does not match in both objects has a
*          greater value in ptr1 than in ptr2 as if evaluated as unsigned char values; And a value less than zero
*          indicates the opposite.
*/
STATIC_INLINE S16 xmemcmp( const void * ptr1, const void * ptr2, U32 size )
{
   const U8 * p1 = (U8 *)ptr1;
   const U8 * p2 = (U8 *)ptr2;
   S16 diff = 0;

   p2 += size;

   while ( size-- )
   {
      p2--;
      diff = (S16)*p1 - (S16)*p2;
      p1++;
      if ( diff != 0 )
      {
         break; /*return diff;*/
      }
   }
   return diff;
}

/** Swap Memory Block.
*
*  The function xmemswap swaps size bytes of beginning at address ptr.
*
*  \param ptr Pointer to the object to be swapped.
*  \param size Number of byte to be swapped.
*
*  \return The xmemswap function returns the pointer to the object.
*/
STATIC_INLINE void * xmemswap( void *ptr, U32 size )
{
   U8 *b = (U8 *)ptr;
   U8 *e = (U8 *)ptr;
   U8 buffer = *b;

   e += size;
   size /= 2U;

   while ( size-- )
   {
      e--;
      *b     = *e;
      *e     = buffer;
      b++;
      buffer = *b;
   }
   return ptr;
}

/** Inverse Memory Move.
*
*  The function xmemmove moves and swaps size bytes from source address to destination address.
*
*  \param dest Pointer to the destination object. It's not save to pass NULL.
*  \param src  Pointer to the source object. It's not save to pass NULL.
*  \param size Number of bytes to be moved inverse.
*
*  \return The xmemmove function returns the value of dest.
*
*/
STATIC_INLINE void * xmemmove( void * dest, void * src, U32 size )
{
   U8 * pDest    = (U8 *)dest;
   U8 * pSrc     = (U8 *) src;
   U32  cpy_size = 0U;
   U8 * pCpyS    = NULL;
   U8 * pCpyT    = NULL;
   U32  swp_size = 0U;
   U8 * pSwpS    = NULL;
   U8 * pSwpT    = NULL;

   if ( pDest >= pSrc )
   {
      cpy_size = ( pDest - pSrc );
      if ( cpy_size < size )
      {
         swp_size = ( size - cpy_size );
      }
      else
      {
         cpy_size = size;
      }
      pCpyS = pSrc;
      pCpyT = pDest + size;
      pSwpS = pSrc  + cpy_size;
      pSwpT = pDest + swp_size;
   }
   else
   {
      cpy_size = ( pSrc - pDest );
      if ( cpy_size < size )
      {
         swp_size = ( size - cpy_size );
      }
      else
      {
         cpy_size = size;
      }
      pCpyS = pSrc  + swp_size;
      pCpyT = pDest + cpy_size;
      pSwpS = pSrc;
      pSwpT = pDest + size;
   }

   /* copy data */
   while ( cpy_size-- )
   {
      pCpyT--;
      *pCpyT = *pCpyS;
      pCpyS++;
   }

   {
      /* swap data */
      U8 swp_buffer = *pSwpS;
      swp_size /= 2U;

      while ( swp_size-- )
      {
         pSwpT--;
         *pSwpS     = *pSwpT;
         *pSwpT     = swp_buffer;
         pSwpS++;
         swp_buffer = *pSwpS;
      }
   }
   return pDest;
}

/*====================================================================================================================*/

#endif

/***********************************************************************************************************************
 *                             Copyright 2011 Linde Material Handling. All rights reserved.                             *
 ***********************************************************************************************************************/
